package com.hivision.iot.api;

import com.alibaba.fastjson.JSON;
import okhttp3.*;
import okhttp3.internal.Util;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @program: CjxjyEquipment
 * @description: 阿里客户端 此类封装调用阿里api
 * @author: Dd7
 * @create: 2023-03-07 13:38
 **/

@Service
public class AliClient {

    @Value("${alihttp.accessKey}")
    private String accessKey;

    @Value("${alihttp.accessSecret}")
    private String accessSecret;

    private final OkHttpClient okHttpClient;

    public AliClient(){
        ThreadPoolExecutor httpClientThreadPool = new ThreadPoolExecutor(100, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
        this.okHttpClient = new OkHttpClient.Builder()
                .dispatcher(new Dispatcher(httpClientThreadPool))
                .connectionPool(new ConnectionPool(5000, 60, TimeUnit.SECONDS))
                .connectTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(120, TimeUnit.SECONDS)
                .readTimeout(120, TimeUnit.SECONDS)
                .retryOnConnectionFailure(true)
                .build();
        this.okHttpClient.dispatcher().setMaxRequests(2000);
        this.okHttpClient.dispatcher().setMaxRequestsPerHost(2000);
    }

    /**
     * 发起HTTP请求
     * @param method 请求方法
     * @param url 请求URL
     * @param body 请求体
     * @return 状态码和响应体
     * @throws IOException IOException
     */
    public Response request(String method, java.net.URL url, Object body) throws IOException {
        // （1）客户端的原始HTTP请求体
        String strBody = "";
        if (body != null) {
            strBody = JSON.toJSONString(body);
        }
        RequestBody reqBody = RequestBody.Companion.create(strBody, MediaType.get("application/json;charset=UTF-8"));

        Request.Builder reqBuilder = new Request.Builder().url(url);
        StringBuilder signMsgBuilder = new StringBuilder();

        //（2）客户端添加Header
        Headers headers = fillHeader(reqBuilder);

        //（3）客户端对Body进行MD5计算
        String contentMd5 = DigestUtils.md5Hex(strBody.getBytes());

        //（4）客户端提取签名串
        //    默认
        signMsgBuilder.append(String.format("%s\n%s\n%s\n%s\n%s", method, headers.get("Accept"), contentMd5, headers.get("Content-Type"), headers.get("Date")));
        //    自定义
        String signHeaders = headers.get("X-Ca-Signature-Headers");
        if (signHeaders != null) {
            String[] sHeaders = signHeaders.replace(" ", "").split(",");
            for (String s : sHeaders) {
                signMsgBuilder.append("\n");
                signMsgBuilder.append(headers.get(s));
            }
        }
        //（5）客户端对签名串签名 使用key 和 签名串 用HmacSHA256加密，再使用Base64加密生成签名signature
        String signature = this.sign(this.accessSecret, signMsgBuilder.toString());;
        reqBuilder.addHeader("X-Ca-Signature", signature);

        //（6）生成请求
        Request request;
        switch (method) {
            case "POST":
                request = reqBuilder.post(reqBody).build();
                break;
            case "PUT":
                request = reqBuilder.put(reqBody).build();
                break;
            case "PATCH":
                request = reqBuilder.patch(reqBody).build();
                break;
            case "DELETE":
                request = reqBuilder.delete(reqBody).build();
                break;
            case "HEAD":
                request = reqBuilder.head().build();
                break;
            case "GET":
            default:
                request = reqBuilder.get().build();
                break;
        }
        return this.okHttpClient.newCall(request).execute();
//        try (Response response = this.okHttpClient.newCall(request).execute()) {
//            return response;
//            // 响应体在这里会自动关闭，‌因为使用了try-with-resources语句
//        } catch (Exception e) {
//            e.printStackTrace();
//            return null;
//        }
    }

    /**
     * 请求构造器填充必要的请求头
     * @param builder 请求构建器
     * @return 请求头
     */
    private Headers fillHeader(Request.Builder builder) {
        Headers.Builder headersBuilder = new Headers.Builder();
        headersBuilder.add("Accept", "application/json");
        headersBuilder.add("Content-Type", "application/json;charset=UTF-8");
        headersBuilder.add("User-Agent", String.format("Honghu-Client/%s", this.accessKey));
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        headersBuilder.add("Date", sdf.format(date));
        headersBuilder.add("X-Ca-Key", this.accessKey);
        headersBuilder.add("X-Ca-Nonce", UUID.randomUUID().toString());
        headersBuilder.add("X-Ca-Timestamp", String.valueOf(date.getTime()));
        headersBuilder.add("X-Ca-Signature-Method", "HmacSHA256");
        headersBuilder.add("X-Ca-Signature-Headers", "X-Ca-Timestamp,X-Ca-Key,X-Ca-Nonce,X-Ca-Signature-Method");
        Headers headers = headersBuilder.build();
        builder.headers(headers);
        return headers;
    }

    /**
     * 得到签名串的签名结果
     * @param accessSecret 签名密钥
     * @param signMessage 签名串
     * @return 签名结果
     */
    private String sign(String accessSecret, String signMessage) {
        try {
            SecretKeySpec signingKey = new SecretKeySpec(accessSecret.getBytes(), "HmacSHA256");
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(signingKey);
            byte[] signMessageByte = null;
            if (signMessage != null && !"".equals(signMessage)) {
                signMessageByte = signMessage.getBytes();
            }
            byte[] doFinal = mac.doFinal(signMessageByte);
            return org.apache.commons.codec.binary.Base64.encodeBase64String(doFinal);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
}
